package com.yjzx.util.common.util.uuid;

import java.io.Serializable;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Enumeration;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author guozhi
 * @date 2025/1/14
 * @description UUID
 */
public class UUID implements Serializable, Comparable<UUID> {
    private static class Holder {
        static final SecureRandom NUMBER_GENERATOR = new SecureRandom();
        static final ThreadLocalRandom THREAD_LOCAL_RANDOM = ThreadLocalRandom.current();

        public static Random getRandom(boolean isSecure) {
            return isSecure ? UUID.Holder.NUMBER_GENERATOR : UUID.Holder.THREAD_LOCAL_RANDOM;
        }
    }

    private static final long serialVersionUID = -4856846361193249489L;
    private final long mostSigBits;
    private final long leastSigBits;

    private UUID(byte[] data) {
        long msb = 0;
        long lsb = 0;
        assert data.length == 16 : "data must be 16 bytes in length";
        for (int i = 0; i < 8; i++) {
            msb = (msb << 8) | (data[i] & 0xff);
        }
        for (int i = 8; i < 16; i++) {
            lsb = (lsb << 8) | (data[i] & 0xff);
        }
        this.mostSigBits = msb;
        this.leastSigBits = lsb;
    }

    public UUID(long mostSigBits, long leastSigBits) {
        this.mostSigBits = mostSigBits;
        this.leastSigBits = leastSigBits;
    }

    /**
     * @return 默认随机uuid
     */
    public static UUID randomUUID() {
        return uuidV4(true);
    }

    /**
     * @return 快速随机uuid
     */
    public static UUID fastUUID() {
        return uuidV4(false);
    }

    /**
     * 根据指定的字节数组生成的uuid
     *
     * @param name 用于构造 UUID
     * @return 从指定数组生成的
     */
    public static UUID nameUUIDFromBytes(byte[] name) {
        return uuidV3(name);
    }

    /**
     * uuid转换为jdk的uuid类型
     *
     * @return jdk uuid
     */
    public java.util.UUID toJdkUuid() {
        return new java.util.UUID(this.mostSigBits, this.leastSigBits);
    }

    /**
     * jdk的uuid类型转换为当前uuid
     *
     * @param uuid jdk的uuid
     * @return 当前uuid
     */
    public static UUID jdkUuidToUuid(java.util.UUID uuid) {
        return new UUID(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
    }

    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-3  </td><td>0-31  </td><td>时间戳（低32位） </td></tr>
     * <tr><td>4-5  </td><td>32-47 </td><td>时间戳（中16位） </td></tr>
     * <tr><td>6    </td><td>48-51 </td><td>版本号（4位）   </td></tr>
     * <tr><td>6    </td><td>52-55 </td><td>时间戳（高4位）  </td></tr>
     * <tr><td>7    </td><td>56-63 </td><td>时间戳（高8位） </td></tr>
     * <tr><td>8    </td><td>64-65 </td><td>变体（2位）    </td></tr>
     * <tr><td>8    </td><td>66-71 </td><td>时钟序列（6位）  </td></tr>
     * <tr><td>9    </td><td>72-79 </td><td>时钟序列（8位） </td></tr>
     * <tr><td>10-15</td><td>80-127</td><td>节点标识符（48位）</td></tr>
     * </table>
     *
     * @return uuid1
     */
    public static UUID uuidV1() {
        long uuidTime = gc100nanoTimestamp();
        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES * 2);
        timestamp.putLong((uuidTime << 32) | ((uuidTime >> 32 & 0xFFFF) << 16) | (uuidTime >> 48 & 0x0FFF));
        timestamp.putLong(((long) Holder.NUMBER_GENERATOR.nextInt() << 48 & 0xFFFF000000000000L) | (0xFFFFFFFFFFFFL & getNodeIdentifier()));
        byte[] value = timestamp.array();
        /* set to version 1     */
        value[6] = (byte) (value[6] | 0x10);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }


    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-3  </td><td>0-31  </td><td>时间戳（低32位） </td></tr>
     * <tr><td>4-5  </td><td>32-47 </td><td>时间戳（中16位） </td></tr>
     * <tr><td>6    </td><td>48-51 </td><td>版本号（4位）   </td></tr>
     * <tr><td>6    </td><td>52-55 </td><td>时间戳（高4位）  </td></tr>
     * <tr><td>7    </td><td>56-63 </td><td>时间戳（高8位） </td></tr>
     * <tr><td>8    </td><td>64-65 </td><td>变体（2位）    </td></tr>
     * <tr><td>8    </td><td>66-71 </td><td>时钟序列（6位）   </td></tr>
     * <tr><td>9    </td><td>72-79 </td><td>时钟序列（8位）  </td></tr>
     * <tr><td>10-15</td><td>90-127</td><td>本地标识符（48位） </td></tr>
     * </table>
     *
     * @return uuid2
     */
    public static UUID uuidV2() {
        return uuidV2(0xF122334455667788L);
    }

    public static UUID uuidV2(long localDomain) {
        long uuidTime = gc100nanoTimestamp();
        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES * 2);
        timestamp.putLong((uuidTime << 32) | ((uuidTime >> 32 & 0xFFFF) << 16) | (uuidTime >> 48 & 0x0FFF));
        timestamp.putLong(((long) Holder.NUMBER_GENERATOR.nextInt(65535) << 48) | (localDomain & 0xFFFF_FFFF_FFFFL));

        byte[] value = timestamp.array();
        /* set to version 2     */
        value[6] = (byte) (value[6] | 0x20);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }

    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-3 </td><td>0-31  </td><td>MD5哈希值（低32位）</td></tr>
     * <tr><td>4-5 </td><td>32-47 </td><td>MD5哈希值（中16位）</td></tr>
     * <tr><td>6   </td><td>48-51 </td><td>版本号（4位）     </td></tr>
     * <tr><td>6   </td><td>52-55 </td><td>MD5哈希值（高4位） </td></tr>
     * <tr><td>7   </td><td>56-63 </td><td>MD5哈希值（高8位） </td></tr>
     * <tr><td>8   </td><td>64-65 </td><td>变体（2位）      </td></tr>
     * <tr><td>8   </td><td>66-71 </td><td>MD5哈希值（高2位） </td></tr>
     * <tr><td>9-15</td><td>72-127</td><td>MD5哈希值（高56位）</td></tr>
     * </table>
     *
     * @param namespace 命名空间
     * @param key       键
     * @return uuid3
     */
    public static UUID uuidV3(String namespace, String key) {
        return uuidV3((namespace + key).getBytes());
    }

    public static UUID uuidV3(byte[] name) {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException var3) {
            throw new InternalError("MD5 not supported");
        }

        byte[] value = md.digest(name);
        /* set to version 3     */
        value[6] = (byte) ((value[6] & 0x0F) | 0x50);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }

    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-3 </td><td>0-31  </td><td>随机数（低32位）</td></tr>
     * <tr><td>4-5 </td><td>32-47 </td><td>随机数（中16位）</td></tr>
     * <tr><td>6   </td><td>48-51 </td><td>版本号（4位） </td></tr>
     * <tr><td>6   </td><td>52-55 </td><td>随机数（高4位）  </td></tr>
     * <tr><td>7   </td><td>56-63 </td><td>随机数（高8位）</td></tr>
     * <tr><td>8   </td><td>64-65 </td><td>变体（2位）   </td></tr>
     * <tr><td>8   </td><td>66-71 </td><td>随机数（高6位） </td></tr>
     * <tr><td>9-15</td><td>72-127</td><td>随机数（高56位）</td></tr>
     * </table>
     *
     * @param isSecure 是否使用安全随机数
     * @return uuid4
     */
    public static UUID uuidV4(boolean isSecure) {
        Random random = Holder.getRandom(isSecure);
        byte[] value = new byte[16];
        random.nextBytes(value);
        /* set to version 4     */
        value[6] = (byte) ((value[6] & 0x0F) | 0x40);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }

    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-3 </td><td>0-31  </td><td>SHA-1哈希值（低32位）</td></tr>
     * <tr><td>4-5 </td><td>32-47 </td><td>SHA-1哈希值（中16位）</td></tr>
     * <tr><td>6   </td><td>48-51 </td><td>版本号（4位） </td></tr>
     * <tr><td>6   </td><td>52-55 </td><td>SHA-1哈希值（高4位）</td></tr>
     * <tr><td>7   </td><td>56-63 </td><td>SHA-1哈希值（高8位）</td></tr>
     * <tr><td>8   </td><td>64-65 </td><td>变体（2位）        </td></tr>
     * <tr><td>8   </td><td>66-71 </td><td>SHA-1哈希值（高6位） </td></tr>
     * <tr><td>9-15</td><td>72-127</td><td>SHA-1哈希值（高56位）</td></tr>
     * </table>
     *
     * @param namespace 命名空间
     * @param key       键
     * @return uuid5
     */
    public static UUID uuidV5(String namespace, String key) {
        return uuidV5((namespace + key).getBytes());
    }

    public static UUID uuidV5(byte[] name) {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException var3) {
            throw new InternalError("SHA1 not supported");
        }

        byte[] value = md.digest(name);
        /* set to version 5     */
        value[6] = (byte) ((value[6] & 0x0F) | 0x50);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }

    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-3  </td><td>0-31  </td><td>时间戳（高32位）</td></tr>
     * <tr><td>4-5  </td><td>32-47 </td><td>时间戳（中16位）</td></tr>
     * <tr><td>6    </td><td>48-51 </td><td>版本号（4位） </td></tr>
     * <tr><td>6    </td><td>52-55 </td><td>时间戳（低4位）  </td></tr>
     * <tr><td>7    </td><td>56-63 </td><td>时间戳（低8位）</td></tr>
     * <tr><td>8    </td><td>64-65 </td><td>变体（2位）   </td></tr>
     * <tr><td>8    </td><td>66-72 </td><td>时钟序列（6位） </td></tr>
     * <tr><td>9    </td><td>72-79 </td><td>时钟序列（8位）</td></tr>
     * <tr><td>10-15</td><td>80-127</td><td>空间唯一标识符（48位） </td></tr>
     * </table>
     *
     * @param isSecure 是否使用安全随机数
     * @return uuid6
     */
    public static UUID uuidV6(boolean isSecure) {

        Random random = Holder.getRandom(isSecure);

        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES * 2);
        long nanoTimestamp = gc100nanoTimestamp();
        timestamp.putLong((nanoTimestamp << 4 & 0xFFFFFFFFFFFF0000L) | (nanoTimestamp & 0x0FFF));
        timestamp.putLong((((long) Holder.NUMBER_GENERATOR.nextInt() << 48) | (random.nextLong() & 0xFFFFFFFFFFFFL)));

        byte[] value = timestamp.array();
        /* set to version 6     */
        value[6] = (byte) (value[6] | 0x60);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }

    /**
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-5 </td><td>0-47  </td><td>时间戳（低48位）</td></tr>
     * <tr><td>6   </td><td>48-51 </td><td>版本号（4位）  </td></tr>
     * <tr><td>6   </td><td>52-55 </td><td>时间戳（高4位） </td></tr>
     * <tr><td>7   </td><td>56-63 </td><td>时间戳（高8位）</td></tr>
     * <tr><td>8    </td><td>64-65 </td><td>变体（2位）   </td></tr>
     * <tr><td>8   </td><td>66-72 </td><td>随机数（6位）  </td></tr>
     * <tr><td>9-15</td><td>72-127</td><td>随机数（高64位）</td></tr>
     * </table>
     * 时间戳加随机数（可至 6429-10-17 10:45:55）
     *
     * @param isSecure 是否使用安全随机数
     * @return uuid7
     */
    public static UUID uuidV7(boolean isSecure) {
        Random random = Holder.getRandom(isSecure);
        // random bytes
        byte[] value = new byte[16];
        random.nextBytes(value);
        // current timestamp in ms
        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES);
        timestamp.putLong(System.currentTimeMillis());
        // timestamp
        System.arraycopy(timestamp.array(), 2, value, 0, 6);
        /* set to version 7     */
        value[6] = (byte) ((value[6] & 0x0F) | 0x70);
        value[8] = (byte) ((value[8] & 0x3F) | 0x80);
        return new UUID(value);
    }

    /**
     * 未实现
     * <table border>
     * <tr><th>字节位置</th><th>位范围  </th><th>描述</th></tr>
     * <tr><td>0-5 </td><td>0-47  </td><td>自定义</td></tr>
     * <tr><td>6   </td><td>48-51 </td><td>版本号（4位）  </td></tr>
     * <tr><td>6   </td><td>52-55 </td><td>自定义</td></tr>
     * <tr><td>7   </td><td>56-63 </td><td>自定义</td></tr>
     * <tr><td>8    </td><td>64-65 </td><td>变体（2位）   </td></tr>
     * <tr><td>8   </td><td>66-72 </td><td>自定义</td></tr>
     * <tr><td>9-15</td><td>72-127</td><td>自定义</td></tr>
     * </table>
     *
     * @return uuid8
     */
    public static UUID uuidV8() {
        throw new UnsupportedOperationException("Not implemented");
    }

    /**
     * 格列高利安历 到现在的100 纳秒 ((1970-01-01) -(1582-10-15))*100
     *
     * @return 1582年10月15日 00：00：00.00（公历改革之日）格列高利安历以来的 100 纳秒间隔计数
     */
    public static long gc100nanoTimestamp() {
        return (System.currentTimeMillis() + 12219321943000L) * 100;
    }

    public static UUID fromString(String name) {
        String[] components = name.split("-");
        if (components.length != 5) {
            throw new IllegalArgumentException("Invalid UUID string: " + name);
        } else {
            for (int i = 0; i < 5; ++i) {
                components[i] = "0x" + components[i];
            }
            long mostSigBits, leastSigBits;
            mostSigBits = Long.decode(components[2]) | Long.decode(components[1]) << 16 | Long.decode(components[0]) << 32;
            leastSigBits = Long.decode(components[4]) | Long.decode(components[3]) << 48;
            return new UUID(mostSigBits, leastSigBits);
        }
    }

    private static long getNodeIdentifier() {
        try {
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = networkInterfaces.nextElement();
                byte[] mac = networkInterface.getHardwareAddress();
                if (mac != null) {
                    long node = 0;
                    for (byte b : mac) {
                        node = (node << 8) | (b & 0xFF);
                    }
                    return node;
                }
            }
        } catch (SocketException ignored) {
        }
        return 0;
    }

    public long getLeastSignificantBits() {
        return this.leastSigBits;
    }

    public long getMostSignificantBits() {
        return this.mostSigBits;
    }

    /**
     * @return uuid转换为byte
     */
    public byte[] getBytes() {
        return ByteBuffer.allocate(16).putLong(mostSigBits).putLong(leastSigBits).array();
    }

    /**
     * @return uuid版本
     */
    public int version() {
        return (int) (this.mostSigBits >> 12 & 0x0f);
    }

    /**
     * @return 变体版本
     */
    public int variant() {
        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62)))
                & (leastSigBits >> 63));
    }

    /**
     * 获取60位时间戳
     *
     * @return
     * @throws UnsupportedOperationException
     */
    @Deprecated
    public long getGc100nanoTimestamp() throws UnsupportedOperationException {
        this.checkTimeBase(1, 2, 6);
        if (this.version() == 6) {
            return ((mostSigBits >> 4) & 0xFFFFFFFFFFFFF000L) | (mostSigBits & 0xFFF);
        }
        return (mostSigBits & 0x0FFFL) << 48
                | ((mostSigBits >> 16) & 0x0FFFFL) << 32
                | mostSigBits >>> 32;
    }


    public long timestamp() {
        this.checkTimeBase(1, 2, 6, 7);
        if (this.version() == 7) {
            return mostSigBits >> 16;
        }
        long uuidTimestamp = getGc100nanoTimestamp();
        // 1582年10月15日的毫秒数
        long gregorianStart = -12219321943000L;

        // 将100纳秒转换为毫秒
        long milliseconds = uuidTimestamp / 100;

        // 调整基准时间
        return milliseconds + gregorianStart;
    }

    /**
     * @return 获取时钟序列
     * @throws UnsupportedOperationException
     */
    @Deprecated
    public int clockSequence() throws UnsupportedOperationException {
        this.checkTimeBase(1);
        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
    }

    /**
     * @return mac地址或唯一标识
     * @throws UnsupportedOperationException
     */
    @Deprecated
    public long node() throws UnsupportedOperationException {
        this.checkTimeBase(1);
        return leastSigBits & 0x0000FFFFFFFFFFFFL;
    }

    @Override
    public String toString() {
        return this.toString(false);
    }

    public String toString(boolean isSimple) {
        StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
        if (!isSimple) {
            builder.append(digits(this.mostSigBits >> 32, 8))
                    .append('-')
                    .append(digits(this.mostSigBits >> 16, 4))
                    .append('-')
                    .append(digits(this.mostSigBits, 4))
                    .append('-')
                    .append(digits(this.leastSigBits >> 48, 4))
                    .append('-')
                    .append(digits(this.leastSigBits, 12));
        } else {
            builder.append(digits(this.mostSigBits>> 32, 8))
                    .append(digits(this.mostSigBits , 8))
                    .append(digits(this.leastSigBits>> 32, 8))
                    .append(digits(this.leastSigBits , 8));
        }
        return builder.toString();
    }

    @Override
    public int hashCode() {
        long hilo = this.mostSigBits ^ this.leastSigBits;
        return (int) (hilo >> 32) ^ (int) hilo;
    }

    @Override
    public boolean equals(Object obj) {
        if (null != obj && obj.getClass() == UUID.class) {
            UUID id = (UUID) obj;
            return this.mostSigBits == id.mostSigBits && this.leastSigBits == id.leastSigBits;
        } else {
            return false;
        }
    }

    @Override
    public int compareTo(UUID val) {
        return (this.mostSigBits < val.mostSigBits ? -1 :
                (this.mostSigBits > val.mostSigBits ? 1 :
                        (Long.compare(this.leastSigBits, val.leastSigBits))));
    }

    private static String digits(long val, int digits) {
        long hi = 1L << digits * 4;
        return Long.toHexString(hi | val & hi - 1L).substring(1);
    }

    private void checkTimeBase(int... version) {
        int thisVersion = this.version();
        for (int j : version) {
            if (thisVersion == j) {
                return;
            }
        }
        throw new UnsupportedOperationException("Not a time-based UUID");
    }
}
